#pragma once
#include "cuda_runtime.h"

#include "util/mgpucontext.h"
#include "helper_timer.h"
#include <tuple>
#include <vector>

#include "cudaEsg.h"
#include "esgSet.h"


namespace cudaEsg
{

	class CudaComputeContext : public ComputeContext
	{
	public:
		CudaComputeContext();
		~CudaComputeContext();


		operation_errors_t ResetStatistics() const;

		operation_errors_t Ratings(int setId, int universeIndex, int weightMatrixId, unsigned char filter, int groupsHeadsCount, int* groupHeads, RatingsResult &result) override;
		static void ComputeRanking(RatingsResult &result, float* ratings_h, ESGSet* set, int clusterGroupHeadsCount, int* clusterGroupHeads, unsigned char* universe, int* companiesMap, unsigned char filter);

		operation_errors_t AddUniverse(int setId, int universeId, int companiesCount, unsigned char * universe) override;
		operation_errors_t DeleteUniverse(int setId, int universeId) override;
		operation_errors_t SetAttributes(int setId, int companiesCount, unsigned char * attributes) override;
		operation_errors_t SetCompanyGroupFlags(int setId, int companiesCount, int attributes_Types_Count, int regionGroupsCount, int peerGroupsCount, int* attributes, int attributesLenght) override;
		//operation_errors_t SetCompanyGroupFlags(int setId, int companiesCount, int attributes_Types_Count, vector<ComparativeGroup> attributes) override;
		operation_errors_t AddMatrix(int setId, int matrixId, int weightsCount, float * weightsMatrix, int profilesCount, unsigned char* profileFlags) override;
		operation_errors_t AddSet(SetConfiguration* configuration) override;
		operation_errors_t SetIndicators(int setId, int companiesCount, int indicatorsCount, float *indicatorScores) override;
		operation_errors_t SetCompaniesScores(int setId, int scoresCount, float *scoresScores) override;
		operation_errors_t SetCompaniesWeights(int setId, int scoresCount, float *weights) override;
		operation_errors_t DeleteSet(int setId) override;
		operation_errors_t DeleteMatrix(int setId, int matrixId) override;
		operation_errors_t CleanContext(int* &ids, int &count) override;
		operation_errors_t FillSetAttributes(int setId, int &companiesCount, int &indicatorsCount, int &clusterGroupsCount, int &clustersCount) override;
		operation_errors_t FillSetAttributes(int setId, int& companiesCount, int& indicatorsCount, int& clusterGroupsCount, int& clustersCount, int& company_grouping_types_count) override;
		operation_errors_t RepeatGroupHeads(int groupsHeadsCount, int clusterCount, int companiesCount, int* groupsHeads, int& repeteadHeadsCount, int*& d_heads) const;
		operation_errors_t SetCombinationModifiers(int setId, int matrixId, int weightsCount, float* weights) override;
		operation_errors_t SetIssueWeights(int setId, int matrixId, int weightsCount, float* weights) override;
		operation_errors_t GovernanceRatings(int setId, int weightMatrixId, bool includeWeights, bool includeWeightedIssues, GovernanceRatingsResult &result) override;

	private:
		StopWatchInterface *_timer;
		mgpu::ContextPtr _mgpuContext;
		int _currentId;

		vector<tuple<int, ESGSet*>>* sets;
		cudaEsg_errors_t CleanSet(ESGSet *set) const;


		void AddMemoryOperation(string name, double elapseSeconds) const;

		int NextId();

		static double CudaComputeContext::getOccupiedMemoryOnDevice()
		{
			// show memory usage of GPU

			size_t free_byte;

			size_t total_byte;

			cudaError_t cuda_status = cudaMemGetInfo(&free_byte, &total_byte);

			if (cudaSuccess != cuda_status){
				std::printf("Error: cudaMemGetInfo fails, %s \n", cudaGetErrorString(cuda_status));

				exit(1);
			}

			double used_db = static_cast<double>(free_byte);

			// printf("GPU memory usage: used = %f, free = %f MB, total = %f MB\n", used_db/1024.0/1024.0, free_db/1024.0/1024.0, total_db/1024.0/1024.0);
			return used_db;
		}

		ESGSet* GetSet(int setId) const;
	};


	

}

